---
title: "Performance Optimization and Best Practices"
---

# Module 13: Performance Optimization and Best Practices

## Overview

In this module, we'll explore advanced performance optimization techniques and best practices in C programming. Unlike JavaScript, where optimization is often handled by the JIT compiler, C gives you direct control over performance at the machine level.

## Learning Objectives

- Understand compiler optimization options and their effects
- Master inline functions and their performance benefits
- Learn cache-friendly code design principles
- Implement algorithm optimization techniques
- Apply memory optimization strategies
- Use performance analysis tools effectively

## Compiler Optimization Options

### JavaScript vs C Optimization

In JavaScript, optimization is primarily handled by the V8 engine's JIT compiler. In C, you have direct control over optimization through compiler flags and code structure.

<UniversalEditor title="Compiler Optimization Comparison" compare={true}>
```javascript !! js
// JavaScript: V8 handles optimization automatically
function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// V8 will optimize this automatically
const result = fibonacci(10);
console.log(result);
```

```c !! c
// C: Manual optimization control
#include <stdio.h>

// Without optimization
int fibonacci_unoptimized(int n) {
    if (n <= 1) return n;
    return fibonacci_unoptimized(n - 1) + fibonacci_unoptimized(n - 2);
}

// With inline optimization
static inline int fibonacci_optimized(int n) {
    if (n <= 1) return n;
    return fibonacci_optimized(n - 1) + fibonacci_optimized(n - 2);
}

int main() {
    // Compile with: gcc -O2 -o program program.c
    int result = fibonacci_optimized(10);
    printf("Fibonacci(10) = %d\n", result);
    return 0;
}
```
</UniversalEditor>

### Common Compiler Flags

<UniversalEditor title="Compiler Optimization Flags" compare={true}>
```javascript !! js
// JavaScript: Node.js optimization flags
// node --max-old-space-size=4096 --optimize-for-size script.js

function processLargeArray(arr) {
    return arr.map(x => x * 2).filter(x => x > 10);
}

const largeArray = Array.from({length: 1000000}, (_, i) => i);
const result = processLargeArray(largeArray);
console.log(result.length);
```

```c !! c
// C: GCC optimization flags
#include <stdio.h>
#include <stdlib.h>

// Compile with different optimization levels:
// gcc -O0 -o program program.c    (no optimization)
// gcc -O1 -o program program.c    (basic optimization)
// gcc -O2 -o program program.c    (more optimization)
// gcc -O3 -o program program.c    (aggressive optimization)
// gcc -Os -o program program.c    (optimize for size)

void process_large_array(int* arr, int size, int* result, int* result_size) {
    int count = 0;
    for (int i = 0; i < size; i++) {
        int doubled = arr[i] * 2;
        if (doubled > 10) {
            result[count++] = doubled;
        }
    }
    *result_size = count;
}

int main() {
    const int size = 1000000;
    int* arr = malloc(size * sizeof(int));
    int* result = malloc(size * sizeof(int));
    int result_size;
    
    // Initialize array
    for (int i = 0; i < size; i++) {
        arr[i] = i;
    }
    
    process_large_array(arr, size, result, &result_size);
    printf("Processed %d elements\n", result_size);
    
    free(arr);
    free(result);
    return 0;
}
```
</UniversalEditor>

## Inline Functions

### JavaScript vs C Inline Functions

JavaScript engines automatically inline functions, while C requires explicit inline declarations.

<UniversalEditor title="Inline Function Comparison" compare={true}>
```javascript !! js
// JavaScript: V8 automatically inlines small functions
function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

// These calls will likely be inlined by V8
let sum = 0;
for (let i = 0; i < 1000000; i++) {
    sum += add(i, 1);
    sum += multiply(i, 2);
}
console.log(sum);
```

```c !! c
// C: Explicit inline functions
#include <stdio.h>

// Inline function declaration
static inline int add(int a, int b) {
    return a + b;
}

static inline int multiply(int a, int b) {
    return a * b;
}

int main() {
    int sum = 0;
    
    // These function calls will be inlined by the compiler
    for (int i = 0; i < 1000000; i++) {
        sum += add(i, 1);
        sum += multiply(i, 2);
    }
    
    printf("Sum: %d\n", sum);
    return 0;
}
```
</UniversalEditor>

### Inline Function Best Practices

<UniversalEditor title="Inline Function Best Practices" compare={true}>
```javascript !! js
// JavaScript: Small functions are automatically optimized
function calculateDistance(x1, y1, x2, y2) {
    const dx = x2 - x1;
    const dy = y2 - y1;
    return Math.sqrt(dx * dx + dy * dy);
}

// Used in tight loops - V8 will optimize
function processPoints(points) {
    let totalDistance = 0;
    for (let i = 0; i < points.length - 1; i++) {
        totalDistance += calculateDistance(
            points[i].x, points[i].y,
            points[i + 1].x, points[i + 1].y
        );
    }
    return totalDistance;
}
```

```c !! c
// C: Explicit inline for performance-critical functions
#include <stdio.h>
#include <math.h>

// Small, frequently called functions are good candidates for inline
static inline double calculate_distance(double x1, double y1, 
                                       double x2, double y2) {
    double dx = x2 - x1;
    double dy = y2 - y1;
    return sqrt(dx * dx + dy * dy);
}

// Avoid inline for large functions
static inline void process_large_data(int* data, int size) {
    // This function is too large for inline - compiler may ignore inline hint
    for (int i = 0; i < size; i++) {
        // Complex processing...
        data[i] = data[i] * 2 + 1;
    }
}

int main() {
    double points[][2] = {{1.0, 1.0}, {2.0, 2.0}, {3.0, 3.0}};
    int num_points = 3;
    
    double total_distance = 0.0;
    for (int i = 0; i < num_points - 1; i++) {
        total_distance += calculate_distance(
            points[i][0], points[i][1],
            points[i + 1][0], points[i + 1][1]
        );
    }
    
    printf("Total distance: %.2f\n", total_distance);
    return 0;
}
```
</UniversalEditor>

## Cache-Friendly Code Design

### Memory Access Patterns

<UniversalEditor title="Cache-Friendly Array Access" compare={true}>
```javascript !! js
// JavaScript: Array access is generally cache-friendly
function processMatrix(matrix) {
    const rows = matrix.length;
    const cols = matrix[0].length;
    let sum = 0;
    
    // Row-major access (cache-friendly in most languages)
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            sum += matrix[i][j];
        }
    }
    
    return sum;
}

const matrix = Array.from({length: 1000}, () => 
    Array.from({length: 1000}, (_, i) => i)
);
console.log(processMatrix(matrix));
```

```c !! c
// C: Explicit cache-friendly design
#include <stdio.h>
#include <stdlib.h>

#define ROWS 1000
#define COLS 1000

// Cache-friendly: Row-major access
int process_matrix_row_major(int matrix[ROWS][COLS]) {
    int sum = 0;
    
    // Access elements in row-major order (cache-friendly)
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            sum += matrix[i][j];
        }
    }
    
    return sum;
}

// Cache-unfriendly: Column-major access
int process_matrix_column_major(int matrix[ROWS][COLS]) {
    int sum = 0;
    
    // Access elements in column-major order (cache-unfriendly)
    for (int j = 0; j < COLS; j++) {
        for (int i = 0; i < ROWS; i++) {
            sum += matrix[i][j];
        }
    }
    
    return sum;
}

int main() {
    int (*matrix)[COLS] = malloc(ROWS * sizeof(*matrix));
    
    // Initialize matrix
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            matrix[i][j] = i + j;
        }
    }
    
    int sum1 = process_matrix_row_major(matrix);
    int sum2 = process_matrix_column_major(matrix);
    
    printf("Row-major sum: %d\n", sum1);
    printf("Column-major sum: %d\n", sum2);
    
    free(matrix);
    return 0;
}
```
</UniversalEditor>

### Data Structure Optimization

<UniversalEditor title="Data Structure Cache Optimization" compare={true}>
```javascript !! js
// JavaScript: Objects and arrays are optimized by the engine
class Particle {
    constructor(x, y, vx, vy, mass) {
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;
        this.mass = mass;
    }
}

// Array of objects - V8 optimizes access patterns
function updateParticles(particles) {
    for (let particle of particles) {
        particle.x += particle.vx;
        particle.y += particle.vy;
    }
}

const particles = Array.from({length: 1000}, () => 
    new Particle(Math.random(), Math.random(), 1, 1, 1)
);
updateParticles(particles);
```

```c !! c
// C: Structure of Arrays (SoA) for better cache performance
#include <stdio.h>
#include <stdlib.h>

// Array of Structures (AoS) - less cache-friendly
typedef struct {
    double x, y, vx, vy, mass;
} Particle_AoS;

// Structure of Arrays (SoA) - more cache-friendly
typedef struct {
    double* x;
    double* y;
    double* vx;
    double* vy;
    double* mass;
    int count;
} Particle_SoA;

void update_particles_aos(Particle_AoS* particles, int count) {
    for (int i = 0; i < count; i++) {
        particles[i].x += particles[i].vx;
        particles[i].y += particles[i].vy;
    }
}

void update_particles_soa(Particle_SoA* particles) {
    for (int i = 0; i < particles->count; i++) {
        particles->x[i] += particles->vx[i];
        particles->y[i] += particles->vy[i];
    }
}

int main() {
    const int count = 1000;
    
    // AoS approach
    Particle_AoS* particles_aos = malloc(count * sizeof(Particle_AoS));
    for (int i = 0; i < count; i++) {
        particles_aos[i].x = (double)rand() / RAND_MAX;
        particles_aos[i].y = (double)rand() / RAND_MAX;
        particles_aos[i].vx = 1.0;
        particles_aos[i].vy = 1.0;
        particles_aos[i].mass = 1.0;
    }
    
    // SoA approach
    Particle_SoA particles_soa = {
        .x = malloc(count * sizeof(double)),
        .y = malloc(count * sizeof(double)),
        .vx = malloc(count * sizeof(double)),
        .vy = malloc(count * sizeof(double)),
        .mass = malloc(count * sizeof(double)),
        .count = count
    };
    
    for (int i = 0; i < count; i++) {
        particles_soa.x[i] = (double)rand() / RAND_MAX;
        particles_soa.y[i] = (double)rand() / RAND_MAX;
        particles_soa.vx[i] = 1.0;
        particles_soa.vy[i] = 1.0;
        particles_soa.mass[i] = 1.0;
    }
    
    update_particles_aos(particles_aos, count);
    update_particles_soa(&particles_soa);
    
    // Cleanup
    free(particles_aos);
    free(particles_soa.x);
    free(particles_soa.y);
    free(particles_soa.vx);
    free(particles_soa.vy);
    free(particles_soa.mass);
    
    return 0;
}
```
</UniversalEditor>

## Algorithm Optimization

### Loop Optimization

<UniversalEditor title="Loop Optimization Techniques" compare={true}>
```javascript !! js
// JavaScript: Loop optimization
function sumArray(arr) {
    let sum = 0;
    const len = arr.length; // Cache length
    
    // Unroll loop for better performance
    for (let i = 0; i < len; i += 4) {
        if (i < len) sum += arr[i];
        if (i + 1 < len) sum += arr[i + 1];
        if (i + 2 < len) sum += arr[i + 2];
        if (i + 3 < len) sum += arr[i + 3];
    }
    
    return sum;
}

const array = Array.from({length: 1000000}, (_, i) => i);
console.log(sumArray(array));
```

```c !! c
// C: Advanced loop optimization
#include <stdio.h>
#include <stdlib.h>

// Loop unrolling for better performance
int sum_array_unrolled(int* arr, int size) {
    int sum = 0;
    int i;
    
    // Unroll loop by 4
    for (i = 0; i < size - 3; i += 4) {
        sum += arr[i] + arr[i + 1] + arr[i + 2] + arr[i + 3];
    }
    
    // Handle remaining elements
    for (; i < size; i++) {
        sum += arr[i];
    }
    
    return sum;
}

// Vectorization-friendly loop
int sum_array_vectorized(int* arr, int size) {
    int sum = 0;
    
    // Simple loop that can be vectorized by compiler
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    
    return sum;
}

int main() {
    const int size = 1000000;
    int* arr = malloc(size * sizeof(int));
    
    for (int i = 0; i < size; i++) {
        arr[i] = i;
    }
    
    int sum1 = sum_array_unrolled(arr, size);
    int sum2 = sum_array_vectorized(arr, size);
    
    printf("Unrolled sum: %d\n", sum1);
    printf("Vectorized sum: %d\n", sum2);
    
    free(arr);
    return 0;
}
```
</UniversalEditor>

### Algorithm Complexity Optimization

<UniversalEditor title="Algorithm Complexity Optimization" compare={true}>
```javascript !! js
// JavaScript: Algorithm optimization
function findDuplicates(arr) {
    const seen = new Set();
    const duplicates = new Set();
    
    for (const item of arr) {
        if (seen.has(item)) {
            duplicates.add(item);
        } else {
            seen.add(item);
        }
    }
    
    return Array.from(duplicates);
}

// O(n) solution instead of O(n²)
const numbers = [1, 2, 3, 4, 2, 5, 6, 3, 7, 8, 1];
console.log(findDuplicates(numbers));
```

```c !! c
// C: Algorithm optimization with hash table
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_SIZE 1000
#define HASH_SIZE 10007

// Simple hash function
unsigned int hash(int key) {
    return key % HASH_SIZE;
}

// Find duplicates using hash table - O(n) instead of O(n²)
int* find_duplicates(int* arr, int size, int* result_size) {
    int* hash_table = calloc(HASH_SIZE, sizeof(int));
    int* result = malloc(size * sizeof(int));
    *result_size = 0;
    
    for (int i = 0; i < size; i++) {
        unsigned int h = hash(arr[i]);
        
        if (hash_table[h] == 0) {
            hash_table[h] = 1;
        } else if (hash_table[h] == 1) {
            // Found duplicate
            result[*result_size] = arr[i];
            (*result_size)++;
            hash_table[h] = 2; // Mark as already added
        }
    }
    
    free(hash_table);
    return result;
}

int main() {
    int arr[] = {1, 2, 3, 4, 2, 5, 6, 3, 7, 8, 1};
    int size = sizeof(arr) / sizeof(arr[0]);
    
    int result_size;
    int* duplicates = find_duplicates(arr, size, &result_size);
    
    printf("Duplicates: ");
    for (int i = 0; i < result_size; i++) {
        printf("%d ", duplicates[i]);
    }
    printf("\n");
    
    free(duplicates);
    return 0;
}
```
</UniversalEditor>

## Memory Optimization Strategies

### Memory Pool Implementation

<UniversalEditor title="Memory Pool Optimization" compare={true}>
```javascript !! js
// JavaScript: Memory management is handled by V8
class MemoryPool {
    constructor(blockSize, poolSize) {
        this.blockSize = blockSize;
        this.poolSize = poolSize;
        this.pool = new ArrayBuffer(poolSize);
        this.freeBlocks = [];
        
        // Pre-allocate blocks
        for (let i = 0; i < poolSize; i += blockSize) {
            this.freeBlocks.push(i);
        }
    }
    
    allocate() {
        if (this.freeBlocks.length === 0) {
            throw new Error('Pool exhausted');
        }
        return this.freeBlocks.pop();
    }
    
    free(offset) {
        this.freeBlocks.push(offset);
    }
}

const pool = new MemoryPool(64, 1024);
const block1 = pool.allocate();
const block2 = pool.allocate();
pool.free(block1);
```

```c !! c
// C: Custom memory pool for performance
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BLOCK_SIZE 64
#define POOL_SIZE 1024
#define MAX_BLOCKS (POOL_SIZE / BLOCK_SIZE)

typedef struct {
    char pool[POOL_SIZE];
    int free_blocks[MAX_BLOCKS];
    int free_count;
    int used_blocks[MAX_BLOCKS];
    int used_count;
} MemoryPool;

MemoryPool* create_memory_pool() {
    MemoryPool* pool = malloc(sizeof(MemoryPool));
    memset(pool->pool, 0, POOL_SIZE);
    
    // Initialize free blocks
    pool->free_count = MAX_BLOCKS;
    for (int i = 0; i < MAX_BLOCKS; i++) {
        pool->free_blocks[i] = i;
    }
    
    pool->used_count = 0;
    return pool;
}

void* pool_allocate(MemoryPool* pool) {
    if (pool->free_count == 0) {
        return NULL; // Pool exhausted
    }
    
    int block_index = pool->free_blocks[--pool->free_count];
    pool->used_blocks[pool->used_count++] = block_index;
    
    return &pool->pool[block_index * BLOCK_SIZE];
}

void pool_free(MemoryPool* pool, void* ptr) {
    char* char_ptr = (char*)ptr;
    int offset = char_ptr - pool->pool;
    int block_index = offset / BLOCK_SIZE;
    
    // Add back to free list
    pool->free_blocks[pool->free_count++] = block_index;
    
    // Remove from used list
    for (int i = 0; i < pool->used_count; i++) {
        if (pool->used_blocks[i] == block_index) {
            pool->used_blocks[i] = pool->used_blocks[--pool->used_count];
            break;
        }
    }
}

void destroy_memory_pool(MemoryPool* pool) {
    free(pool);
}

int main() {
    MemoryPool* pool = create_memory_pool();
    
    // Allocate blocks
    void* block1 = pool_allocate(pool);
    void* block2 = pool_allocate(pool);
    
    if (block1 && block2) {
        strcpy((char*)block1, "Hello");
        strcpy((char*)block2, "World");
        
        printf("Block1: %s\n", (char*)block1);
        printf("Block2: %s\n", (char*)block2);
        
        pool_free(pool, block1);
        pool_free(pool, block2);
    }
    
    destroy_memory_pool(pool);
    return 0;
}
```
</UniversalEditor>

## Performance Analysis Tools

### Profiling and Benchmarking

<UniversalEditor title="Performance Analysis Tools" compare={true}>
```javascript !! js
// JavaScript: Performance measurement
function measurePerformance(fn, iterations = 1000000) {
    const start = performance.now();
    
    for (let i = 0; i < iterations; i++) {
        fn();
    }
    
    const end = performance.now();
    return end - start;
}

function optimizedFunction() {
    let sum = 0;
    for (let i = 0; i < 1000; i++) {
        sum += i;
    }
    return sum;
}

function unoptimizedFunction() {
    let sum = 0;
    for (let i = 0; i < 1000; i++) {
        sum = sum + i; // Less optimized
    }
    return sum;
}

const time1 = measurePerformance(optimizedFunction);
const time2 = measurePerformance(unoptimizedFunction);

console.log(`Optimized: ${time1.toFixed(2)}ms`);
console.log(`Unoptimized: ${time2.toFixed(2)}ms`);
```

```c !! c
// C: Performance measurement and profiling
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>

// High-resolution timer
double get_time() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + tv.tv_usec * 1e-6;
}

// Performance measurement function
double measure_performance(int (*func)(), int iterations) {
    double start_time = get_time();
    
    for (int i = 0; i < iterations; i++) {
        func();
    }
    
    double end_time = get_time();
    return (end_time - start_time) * 1000.0; // Convert to milliseconds
}

// Optimized function
int optimized_function() {
    int sum = 0;
    for (int i = 0; i < 1000; i++) {
        sum += i;
    }
    return sum;
}

// Unoptimized function
int unoptimized_function() {
    int sum = 0;
    for (int i = 0; i < 1000; i++) {
        sum = sum + i; // Less optimized
    }
    return sum;
}

int main() {
    const int iterations = 1000000;
    
    double time1 = measure_performance(optimized_function, iterations);
    double time2 = measure_performance(unoptimized_function, iterations);
    
    printf("Optimized: %.2f ms\n", time1);
    printf("Unoptimized: %.2f ms\n", time2);
    printf("Speedup: %.2fx\n", time2 / time1);
    
    return 0;
}
```
</UniversalEditor>

## Common Pitfalls and Solutions

### Performance Anti-patterns

<UniversalEditor title="Performance Anti-patterns" compare={true}>
```javascript !! js
// JavaScript: Performance anti-patterns
function inefficientStringConcatenation() {
    let result = '';
    for (let i = 0; i < 10000; i++) {
        result += 'x'; // Creates new string each time
    }
    return result;
}

function efficientStringConcatenation() {
    const parts = [];
    for (let i = 0; i < 10000; i++) {
        parts.push('x');
    }
    return parts.join(''); // Single concatenation
}

// Measure the difference
const start1 = performance.now();
inefficientStringConcatenation();
const end1 = performance.now();

const start2 = performance.now();
efficientStringConcatenation();
const end2 = performance.now();

console.log(`Inefficient: ${(end1 - start1).toFixed(2)}ms`);
console.log(`Efficient: ${(end2 - start2).toFixed(2)}ms`);
```

```c !! c
// C: Performance anti-patterns and solutions
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

// Inefficient: Repeated strlen calls
int inefficient_string_processing(char* str) {
    int count = 0;
    for (int i = 0; i < strlen(str); i++) { // strlen called every iteration
        if (str[i] == 'a') count++;
    }
    return count;
}

// Efficient: Cache strlen result
int efficient_string_processing(char* str) {
    int count = 0;
    int len = strlen(str); // Call once
    for (int i = 0; i < len; i++) {
        if (str[i] == 'a') count++;
    }
    return count;
}

// Inefficient: Unnecessary function calls in loops
int inefficient_loop() {
    int sum = 0;
    for (int i = 0; i < 1000000; i++) {
        sum += rand() % 100; // Function call in loop
    }
    return sum;
}

// Efficient: Cache function results
int efficient_loop() {
    int sum = 0;
    for (int i = 0; i < 1000000; i++) {
        // Use cached values or inline calculations
        sum += i % 100;
    }
    return sum;
}

int main() {
    char test_string[] = "This is a test string with many characters";
    
    clock_t start, end;
    double cpu_time_used;
    
    // Test string processing
    start = clock();
    for (int i = 0; i < 100000; i++) {
        inefficient_string_processing(test_string);
    }
    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("Inefficient string processing: %.3f seconds\n", cpu_time_used);
    
    start = clock();
    for (int i = 0; i < 100000; i++) {
        efficient_string_processing(test_string);
    }
    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("Efficient string processing: %.3f seconds\n", cpu_time_used);
    
    return 0;
}
```
</UniversalEditor>

## Exercises

### Exercise 1: Loop Optimization
Optimize the following C function for better performance:

```c
int sum_array_slow(int* arr, int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum = sum + arr[i];
    }
    return sum;
}
```

### Exercise 2: Memory Access Pattern
Rewrite the following function to be more cache-friendly:

```c
void process_matrix_slow(int matrix[1000][1000]) {
    for (int j = 0; j < 1000; j++) {
        for (int i = 0; i < 1000; i++) {
            matrix[i][j] *= 2;
        }
    }
}
```

### Exercise 3: Algorithm Optimization
Optimize this O(n²) algorithm to O(n log n) or better:

```c
int find_max_difference(int* arr, int size) {
    int max_diff = 0;
    for (int i = 0; i < size; i++) {
        for (int j = i + 1; j < size; j++) {
            int diff = abs(arr[i] - arr[j]);
            if (diff > max_diff) {
                max_diff = diff;
            }
        }
    }
    return max_diff;
}
```

## Performance Analysis Summary

### Key Optimization Techniques

1. **Compiler Optimization**: Use appropriate compiler flags (-O2, -O3)
2. **Inline Functions**: Use `static inline` for small, frequently called functions
3. **Cache-Friendly Design**: Access memory in sequential patterns
4. **Loop Optimization**: Unroll loops and avoid function calls in loops
5. **Memory Management**: Use memory pools for frequent allocations
6. **Algorithm Selection**: Choose appropriate algorithms for your use case

### Performance Measurement

- Use `clock()` or `gettimeofday()` for timing
- Profile with tools like `gprof` or `perf`
- Measure both time and memory usage
- Test with realistic data sizes
- Consider cache effects and memory access patterns

### Best Practices

- Profile before optimizing
- Focus on the most critical code paths
- Use appropriate data structures
- Avoid premature optimization
- Test optimizations with real data
- Document performance characteristics

## Next Steps

In the next module, we'll explore advanced topics including:
- Multi-threading and concurrency
- Advanced memory management techniques
- SIMD optimizations
- Platform-specific optimizations
- Real-time programming considerations

This module has provided you with the fundamental tools and techniques for optimizing C code. Remember that the best optimization is often choosing the right algorithm and data structure for your specific use case. 